﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Fluq;
using System.Reflection;

namespace Fluq
{
	public sealed class DependencyComposer : IDependencyComposer
	{
		public bool TryCompose(object graphId, IDependencyContainer container, Type type, IEnumerable<IDependencyParameter> parameters, out object instance)
		{
			if (graphId == null)
			{
				throw new ArgumentNullException("graphId");
			}

			if (container == null)
			{
				throw new ArgumentNullException("container");
			}

			if (type == null)
			{
				throw new ArgumentNullException("type");
			}

			if (parameters == null)
			{
				throw new ArgumentNullException("arguments");
			}

			foreach (ConstructorInfo constructor in type.GetConstructors().OrderByDescending(ctor => ctor.GetParameters().Length))
			{
				ParameterInfo[] constructorParameters = constructor.GetParameters();

				object[] parameterInstances = new object[constructorParameters.Length];

				bool success = true;

				for (int i = 0; i < constructorParameters.Length; i++)
				{
					ParameterInfo constructorParameter = constructorParameters[i];

					IDependencyParameter parameter = null;

					object parameterInstance;

					if (parameters != null)
					{
						parameter = parameters.FirstOrDefault(p => p.Match(constructorParameter));
					}
					
					if (parameter != null)
					{
						parameterInstances[i] = parameter.Factory(container);
					}
					else
					{
						if (container.Resolver.TryResolve(graphId, container, new CompositionKey(constructorParameter.ParameterType), out parameterInstance))
						{
							parameterInstances[i] = parameterInstance;
						}
						else
						{
							success = false;

							break;
						}
					}
				}

				if (success)
				{
					instance = constructor.Invoke(parameterInstances);

					return true;
				}
			}

			instance = null;

			return false;
		}

		public bool TryDestroy(object instance)
		{
			if (instance != null)
			{
				IDisposable disposable = instance as IDisposable;

				if (disposable != null)
				{
					disposable.Dispose();
				}

				return true;
			}
			else
			{
				return false;
			}
		}

		public bool TryCompose(IDependencyContainer container, Type type, IEnumerable<IDependencyParameter> arguments, out object instance)
		{
			object graphId = new object();

			IDependencyComposer composer = container.Composer;

			ContextualDependencyResolver contextualResolver = new ContextualDependencyResolver(graphId, container.Resolver);
			ContextualDependencyComposer contextualComposer = new ContextualDependencyComposer(graphId, this);

			container = new DependencyContainer(container.Registry, contextualResolver, contextualComposer, container.Lifetime);

			return composer.TryCompose(graphId, container, type, arguments, out instance);
		}
	}
}
